import * as acorn from 'acorn';

import store from '../../store';
import { getFunctionName } from './utils';
import { splicCommentForFunction } from '../../comment';
import { readFileContent, getFileExt, writeFileContent, getEndSpaceLen } from '../../utils';
import { isObjectExpression, isFunctionDeclaration, isExportDefaultDeclaration, isProgram, Node, isObjectMethod } from '@babel/types';

import { IComments } from '../../types/normal';
import { IScriptParseResultTurnBack } from '../../types/script';

export default turnBackDoc;

/**
 * 根据配置还原注释
 */
function turnBackDoc(filepath: string, configs: IScriptParseResultTurnBack) {
    try {
        const ext = getFileExt(filepath);
        const fileType = ext === 'js' ? 'javascript' : ext === 'ts' ? 'typescript' : ext;

        if (fileType === 'javascript' || fileType === 'typescript') {
            if (fileType === 'javascript') {
                const code = readFileContent(filepath);
                if (code) {
                    const newCode = turnBackJavascriptDoc(code, configs);
                    newCode && writeFileContent(filepath, newCode);
                }
            }
            store.resetRandomCodeOneCharIndex();

            // ...
        } else {
            throw new Error('仅支持 JS/TS 文件');
        }

        // ...
    } catch (error) {
        store.resetRandomCodeOneCharIndex();
        throw error;
    }
}

/**
 * 还原 JS 文件注释
 */
function turnBackJavascriptDoc(code: string, configs: IScriptParseResultTurnBack) {
    if (configs.functions) {
        let codeRet = code;
        const { functions } = configs;

        // 记录字符相对于源码的新增数
        let addTextLen = 0;

        // 记录现有注释的信息
        // 以注释结尾的下一行号作为 key
        // 因为注释结尾的下一行往往是其函数的主体
        const lineMap: IComments = {};

        const rootNode = acorn.parse(code, {
            locations: true,
            sourceType: 'module',
            ecmaVersion: 'latest',
            onComment(isBlock, text, start, end, startPos, endPos) {
                if (endPos) {
                    lineMap['' + (endPos.line + 1)] = {
                        isBlockComment: isBlock,
                        commentStart: start,
                        commentEnd: end,
                        commentText: text,
                        commentStartPos: startPos,
                        commentEndPos: endPos,
                        commentLength: isBlock ? text.length + 4 : text.length + 2
                    };
                }
            }
        });

        if (isProgram(rootNode)) {
            // ...
            const turnBackFunctionComment = (node: Node) => {
                if (functions) {
                    const functionName = getFunctionName(node);
                    const functionStartLine = '' + node.loc?.start.line;

                    if (functionStartLine && functionName !== null && functionName in functions) {
                        // ...
                        // 有配置项
                        if (functions[functionName]) {
                            // 修改注释
                            if (functionStartLine in lineMap) {
                                const target = lineMap[functionStartLine];
                                if (target.commentStartPos && target.commentEndPos) {
                                    const startCode = codeRet.substring(0, target.commentStart + addTextLen);
                                    const endCode = codeRet.substring(target.commentEnd + addTextLen);

                                    const spaceLen = getEndSpaceLen(startCode);
                                    // @ts-ignore
                                    const commentText = splicCommentForFunction(functions[functionName], { spaceLen });

                                    codeRet = `${startCode}${commentText}${endCode}`;
                                    addTextLen += commentText.length - target.commentLength;
                                }

                                // ...
                            } else {
                                // 添加注释
                                if (node.start !== null) {
                                    const startCode = codeRet.substring(0, node.start + addTextLen);
                                    const endCode = codeRet.substring(node.start + addTextLen);

                                    const spaceLen = getEndSpaceLen(startCode);
                                    // @ts-ignore
                                    let commentText = splicCommentForFunction(functions[functionName], { spaceLen });
                                    commentText += '\n';

                                    codeRet = `${startCode}${commentText}${endCode}`;
                                    addTextLen += commentText.length;
                                }
                            }

                            // ...
                        } else {
                            // 删除注释
                            if (functionStartLine in lineMap) {
                                const target = lineMap[functionStartLine];
                                if (target.commentStartPos && target.commentEndPos) {
                                    const startCode = codeRet.substring(0, target.commentStart + addTextLen - 1);
                                    const endCode = codeRet.substring(target.commentEnd + addTextLen);
                                    codeRet = `${startCode}${endCode}`;
                                    addTextLen -= target.commentLength;
                                }
                            }
                        }
                    }
                }
            };

            rootNode.body.forEach((node: Node) => {
                if (isFunctionDeclaration(node)) {
                    turnBackFunctionComment(node);
                } else if (isExportDefaultDeclaration(node)) {
                    if (isObjectExpression(node.declaration)) {
                        for (const n of node.declaration.properties) {
                            // @ts-ignore
                            if (n.method) {
                                turnBackFunctionComment(n);
                            }
                        }
                    } else if (isFunctionDeclaration(node.declaration)) {
                        turnBackFunctionComment(node);
                    }
                }
            });
        }

        return codeRet;
    }
}
